﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.ESB.Itinerary.OM.V1;
using System.IO;
using Microsoft.BizTalk.Streaming;
using Microsoft.Practices.ESB.Exception.Management;
using System.Reflection;
using System.Diagnostics;
using Microsoft.BizTalk.Bam.EventObservation;
using System.Xml;
using ESB.BAM.BLogic;
using System.Xml.XPath;

namespace ESB.BAM.ItineraryManager
{
    /// <summary>
    /// This class overrides the default ESB itinerary manager to add the BAM handling of the itinerary service advance events
    /// </summary>
    [Serializable]
    public class BAMItineraryManager : Microsoft.Practices.ESB.Itinerary.OM.V1.ItineraryV1, Microsoft.Practices.ESB.Itinerary.IItinerary
    {

        #region Fields
        string _RawMsgBody = "";
        bool _bIsBodyAlreadyTracked = false;
        static bool _bIsNotifiedAdapterWarning = false;
        #endregion

        #region Properties

        #endregion

        #region Methods
        /// <summary>
        /// Gets the message stream.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private Stream GetMessageStream(Microsoft.BizTalk.Message.Interop.IBaseMessage msg, Microsoft.BizTalk.Component.Interop.IPipelineContext context)
        {
            Stream stream = msg.BodyPart.GetOriginalDataStream();

            if (!stream.CanSeek)
            {
                ReadOnlySeekableStream readStream = new ReadOnlySeekableStream(stream);

                if (context != null)
                {
                    context.ResourceTracker.AddResource(readStream);
                }

                msg.BodyPart.Data = readStream;
                stream = readStream;
            }
            return stream;
        }

        /// <summary>
        /// Gets the message stream.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private Stream GetMessageStream(Microsoft.XLANGs.BaseTypes.XLANGMessage msg, Microsoft.BizTalk.Component.Interop.IPipelineContext context)
        {
            if (msg.Count <= 0) throw new ArgumentException("XLANGMessage does not have a body part");
            Stream stream = msg[0].RetrieveAs(typeof(Stream)) as Stream;

            if (!stream.CanSeek)
            {
                ReadOnlySeekableStream readStream = new ReadOnlySeekableStream(stream);

                stream = readStream;
            }
            return stream;
        }

        /// <summary>
        /// Handles the BAM advance.
        /// This is the main interceptor method and it will get if we have a tracking profile mapping for the current itinerary service and call the internal method
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="context">The context.</param>
        /// <param name="step">The step.</param>
        /// <param name="TimeStamp">The time stamp.</param>
        private void HandleBAMAdvance(Microsoft.BizTalk.Message.Interop.IBaseMessage message, Microsoft.BizTalk.Component.Interop.IPipelineContext context, Microsoft.Practices.ESB.Itinerary.IItineraryStep step, DateTime TimeStamp)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn();
            Stream MsgStream = null;
            try
            {
                ItineraryServices service = ItineraryData.Services[step.Position];
                if (service == null || !service.Service.isTrackingEnabled) return;
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceInfo("Getting the tracking profile step for {0}, {1}.", ItineraryData.name, service.Service.businessName);
                EsbTrackingProfileStep ProfileStep = EsbTrackingProfileStep.GetProfileStep(ItineraryData.name, service.Service.businessName);
                if (ProfileStep == null) return;
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceInfo("Got the tracking profile step for {0}, {1} mapped to Milestone {2}.", ItineraryData.name, service.Service.businessName, ProfileStep.MilestoneName);
                EventStream BAMStream = context.GetEventStream();
                MsgStream = GetMessageStream(message, context);
                DateTime AdapterReceiveTime = DateTime.MinValue;
                try
                {
                    AdapterReceiveTime = (DateTime)message.Context.Read("AdapterReceiveCompleteTime", "http://schemas.microsoft.com/BizTalk/2003/messagetracking-properties");
                }
                catch (NullReferenceException ex)
                {
                    if (!_bIsNotifiedAdapterWarning)
                    {
                        _bIsNotifiedAdapterWarning = true;
                        EventLogger.LogMessage("Cannot get AdapterReceiveCompleteTime because BizTalk tracking is turned on.\r\n" + EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), ex), EventLogEntryType.Warning, 0x17cb);
                    }
                }
                HandleBAMAdvanceInternal(ProfileStep, MsgStream, BAMStream, step, TimeStamp, AdapterReceiveTime);
            }
            catch (Exception ex)
            {
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceError(ex, true, callToken);
                EventLogger.LogMessage(EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), ex), EventLogEntryType.Error, 0x17cb);
            }
            finally
            {
                if (MsgStream != null && MsgStream.Position != 0)
                    MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Handles the BAM initialize.
        /// This is called before the execution of the first itinerary service in the itinerary and it is used to
        /// 1- Get the BAM activity id.
        /// 2- Track the raw message body if required.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="TimeStamp">The time stamp.</param>
        private void HandleBAMInitialize(Microsoft.BizTalk.Message.Interop.IBaseMessage message, DateTime TimeStamp)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn();
            Stream MsgStream = null;
            try
            {
                if (ItineraryData.ServiceInstance.position == 0)
                {
                    MsgStream = GetMessageStream(message, null);
                    // First Init the Id
                    EsbTrackingProfileStep ProfileStep = EsbTrackingProfileStep.GetProfileStep(ItineraryData.name, "_");
                    if (ProfileStep == null || ProfileStep.Action != ItineraryServiceActionEnum.InitId)
                    {
                        throw new ApplicationException("Cannot find proper itinerary ID initializer profile step.");
                    }
                    HandleBAMInitializeInternal(ProfileStep, MsgStream);
                    if (MsgStream != null && MsgStream.Position != 0)
                        MsgStream.Seek(0, SeekOrigin.Begin);
                    // Now track the msg  body if required
                    ItineraryServices service = ItineraryData.Services[0];
                    if (service == null || !service.Service.isTrackingEnabled) return;
                    ProfileStep = EsbTrackingProfileStep.GetProfileStep(ItineraryData.name, service.Service.businessName);
                    if (ProfileStep == null) return;
                    DateTime AdapterReceiveTime = DateTime.MinValue;
                    try
                    {
                        AdapterReceiveTime = (DateTime)message.Context.Read("AdapterReceiveCompleteTime", "http://schemas.microsoft.com/BizTalk/2003/messagetracking-properties");
                    }
                    catch (NullReferenceException ex)
                    {
                        if (!_bIsNotifiedAdapterWarning)
                        {
                            _bIsNotifiedAdapterWarning = true;
                            EventLogger.LogMessage("Cannot get AdapterReceiveCompleteTime because BizTalk tracking is turned on.\r\n" + EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), ex), EventLogEntryType.Warning, 0x17cb);
                        }
                    }
                    HandleBAMInitializeDataInternal(ProfileStep, MsgStream, TimeStamp, AdapterReceiveTime);
                }
            }
            catch (Exception ex)
            {
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceError(ex, true, callToken);
                EventLogger.LogMessage(EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), ex.GetBaseException()), EventLogEntryType.Error, 0x17cb);
            }
            finally
            {
                if (MsgStream != null && MsgStream.Position != 0)
                    MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Handles the BAM initialize internal.
        /// This is used as the initialization steps at the start of each itinerary
        /// it will get the BAM activity id to be used to link to the tracking framework from the message and writes that to the itinerary header
        /// it is mandatory to have at least one of these mappings with the init action type for each itinerary tracked or it will not be linked
        /// </summary>
        /// <param name="ProfileStep">The profile step.</param>
        /// <param name="MsgStream">The MSG stream.</param>
        private void HandleBAMInitializeInternal(EsbTrackingProfileStep ProfileStep, Stream MsgStream)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn(ProfileStep);
            // Prepare the XML reader to be used to get data from the input message
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                ConformanceLevel = ConformanceLevel.Document,
                IgnoreWhitespace = true,
                ValidationType = ValidationType.None,
                IgnoreProcessingInstructions = true,
                IgnoreComments = true,
                CloseInput = false
            };
            XmlReader reader = XmlReader.Create(MsgStream, settings);
            string id = ItineraryData.uuid;
            if (!string.IsNullOrEmpty(ProfileStep.MsgIdXPath))
            {
                string BusinessId = ExtractDataValueXPath(settings, MsgStream, ProfileStep.MsgIdXPath);
                if (!string.IsNullOrEmpty(BusinessId))
                {
                    id = BusinessId;
                }
            }
            WriteProperty("BAMActivityId", id);
            if (MsgStream.Position != 0)
            {
                MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Handles the BAM itinerary service advance event.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="step">The step.</param>
        /// <param name="TimeStamp">The time stamp.</param>
        private void HandleBAMAdvance(Microsoft.XLANGs.BaseTypes.XLANGMessage message, Microsoft.Practices.ESB.Itinerary.IItineraryStep step, DateTime TimeStamp)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn(step, TimeStamp);
            Stream MsgStream = null;
            try
            {
                ItineraryServices service = ItineraryData.Services[step.Position];
                if (service == null || !service.Service.isTrackingEnabled) return;
                EsbTrackingProfileStep ProfileStep = EsbTrackingProfileStep.GetProfileStep(ItineraryData.name, service.Service.businessName);
                if (ProfileStep == null) return;
                // TODO: check weather we can get the adapter receive time stamp from the XLANGMessage
                MsgStream = GetMessageStream(message, null);
                HandleBAMAdvanceInternal(ProfileStep, MsgStream, null, step, TimeStamp, DateTime.MinValue);
            }
            catch (Exception ex)
            {
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceError(ex, true, callToken);
                EventLogger.LogMessage(EventFormatter.FormatEvent(MethodBase.GetCurrentMethod(), ex), EventLogEntryType.Error, 0x17cb);
            }
            finally
            {
                if (MsgStream != null && MsgStream.Position != 0)
                    MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Handles the BAM initialize data internal.
        /// We only use this function to track the raw message body coming on the wire before the execution of the initial itinerary step
        /// for this to work you HAVE to map the first itinerary service to a start activity action using the mapping tables.
        /// </summary>
        /// <param name="ProfileStep">The profile step.</param>
        /// <param name="MsgStream">The MSG stream.</param>
        /// <param name="TimeStamp">The time stamp.</param>
        /// <param name="AdapterReceiveTime">The adapter receive time.</param>
        private void HandleBAMInitializeDataInternal(EsbTrackingProfileStep ProfileStep, Stream MsgStream, DateTime TimeStamp, DateTime AdapterReceiveTime)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn(ProfileStep, TimeStamp, AdapterReceiveTime);
            if (ProfileStep == null || ProfileStep.Id <= 0)
            {
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
                return;
            }
            List<ESBTrackingDataStep> lstData = ESBTrackingDataStep.GetDataAtStep(ProfileStep.Id);
            if (lstData == null || lstData.Count <= 0)
            {
                Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
                return;
            }
            ItineraryServices service = ItineraryData.Services[0];
            // Prepare the XML reader to be used to get data from the input message
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                ConformanceLevel = ConformanceLevel.Document,
                IgnoreWhitespace = true,
                ValidationType = ValidationType.None,
                IgnoreProcessingInstructions = true,
                IgnoreComments = true,
                CloseInput = false
            };
            XmlReader reader = XmlReader.Create(MsgStream, settings);
            // Get the activity Id from the itinerary property BAMActivityId
            string id = ReadProperty("BAMActivityId");
            if (ProfileStep.Action != ItineraryServiceActionEnum.Start)
            {
                throw new ApplicationException("The first step should always be a start activity step!");
            }
            if (lstData != null && lstData.Count > 0)
            {
                // We have data to track so track it
                foreach (ESBTrackingDataStep dStep in lstData)
                {
                    if (dStep.DataExtractionXPath.ToUpper() == "BODY")
                    {
                        // This is a body tracking request so add an activity reference
                        // This will be stored to be added later when we already have started the activity
                        string strMsgBody = "";
                        if (reader.Read())
                        {
                            strMsgBody = reader.ReadOuterXml();
                        }
                        MsgStream.Seek(0, SeekOrigin.Begin);
                        _RawMsgBody = strMsgBody;
                        _bIsBodyAlreadyTracked = true;
                    }
                }
            }
            if (MsgStream.Position != 0)
            {
                MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Handles the BAM advance internal.
        /// </summary>
        /// <param name="ProfileStep">The profile step.</param>
        /// <param name="MsgStream">The message stream.</param>
        /// <param name="BAMStream">The BAM stream.</param>
        /// <param name="step">The itinerary step.</param>
        /// <param name="TimeStamp">The current time stamp.</param>
        /// <param name="AdapterReceiveTime">The adapter receive time.</param>
        private void HandleBAMAdvanceInternal(EsbTrackingProfileStep ProfileStep, Stream MsgStream, EventStream BAMStream, Microsoft.Practices.ESB.Itinerary.IItineraryStep step, DateTime TimeStamp, DateTime AdapterReceiveTime)
        {
            var callToken = Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceIn(ProfileStep, step, TimeStamp, AdapterReceiveTime);
            ItineraryServices service = ItineraryData.Services[step.Position];
            // Prepare the XML reader to be used to get data from the input message
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                ConformanceLevel = ConformanceLevel.Document,
                IgnoreWhitespace = true,
                ValidationType = ValidationType.None,
                IgnoreProcessingInstructions = true,
                IgnoreComments = true,
                CloseInput = false
            };
            XmlReader reader = XmlReader.Create(MsgStream, settings);
            // Initialize the activity id either as the itinerary uuid or read it from the itinerary manager stored property BAMActivityId
            string id = ItineraryData.uuid;
            string storedid = ReadProperty("BAMActivityId");
            bool bIsStartingWithBusId = false;
            if (!string.IsNullOrEmpty(storedid))
            {
                id = storedid;
            }
            // Check if we have a non empty XPath to use to get the message Id from the body of the message (Business Message Id)
            // Valid only for start or change of Id actions
            if (!string.IsNullOrEmpty(ProfileStep.MsgIdXPath) && (ProfileStep.Action == ItineraryServiceActionEnum.ChangeId) || (ProfileStep.Action == ItineraryServiceActionEnum.Start))
            {
                string BusinessId = ExtractDataValueXPath(settings, MsgStream, ProfileStep.MsgIdXPath);
                if (!string.IsNullOrEmpty(BusinessId))
                {
                    if (ProfileStep.Action == ItineraryServiceActionEnum.ChangeId)
                    {
                        // This is a change of Id so enable continuation on the previous activity Id and end it
                        ESB.BAM.Wrappers.BAMAPIWrapper.EnableContinuation(BAMStream, ProfileStep.ActivityName, id, BusinessId);
                        ESB.BAM.Wrappers.BAMAPIWrapper.EndActivity(BAMStream, ProfileStep.ActivityName, id);
                    }
                    WriteProperty("BAMActivityId", BusinessId);
                    id = BusinessId;
                    if (ProfileStep.Action == ItineraryServiceActionEnum.Start)
                        bIsStartingWithBusId = true;
                }
            }
            // The endifEmpty action is only valid for orchestrations not itineraries
            if (ProfileStep.Action == ItineraryServiceActionEnum.EndIfEmpty)
            {
                ProfileStep.Action = ItineraryServiceActionEnum.End;
            }
            switch (ProfileStep.Action)
            {
                case ItineraryServiceActionEnum.Start:
                    if (!bIsStartingWithBusId)
                    {
                        // Starting an activity with the itinerary uuid
                        ESB.BAM.Wrappers.BAMAPIWrapper.StartActivity(BAMStream, ProfileStep.ActivityName, id);
                    }
                    else
                    {
                        // Starting an activity with the real business Id but since this might cause collisions specially for UWP as it does not have a unique business Id for some cases
                        // will start the activity also with the itinerary uuid and enable continuation with the real business Id
                        ESB.BAM.Wrappers.BAMAPIWrapper.StartActivity(BAMStream, ProfileStep.ActivityName, ItineraryData.uuid);
                        ESB.BAM.Wrappers.BAMAPIWrapper.EnableContinuation(BAMStream, ProfileStep.ActivityName, ItineraryData.uuid, id);
                        ESB.BAM.Wrappers.BAMAPIWrapper.EndActivity(BAMStream, ProfileStep.ActivityName, ItineraryData.uuid);
                    }
                    if (AdapterReceiveTime != DateTime.MinValue)
                    {
                        // Was able to get the adapter completion time
                        TimeStamp = AdapterReceiveTime;
                    }
                    ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivity(BAMStream, ProfileStep.ActivityName, id, ProfileStep.MilestoneName, TimeStamp);
                    break;
                case ItineraryServiceActionEnum.ChangeId:
                case ItineraryServiceActionEnum.Update:
                    ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivity(BAMStream, ProfileStep.ActivityName, id, ProfileStep.MilestoneName, TimeStamp);
                    break;
                case ItineraryServiceActionEnum.End:
                    ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivity(BAMStream, ProfileStep.ActivityName, id, ProfileStep.MilestoneName, TimeStamp);
                    break;
            }
            List<ESBTrackingDataStep> lstData = ESBTrackingDataStep.GetDataAtStep(ProfileStep.Id);
            if (lstData != null && lstData.Count > 0)
            {
                // We have data to track so track it
                foreach (ESBTrackingDataStep dStep in lstData)
                {
                    if (dStep.DataExtractionXPath.ToUpper() == "BODY")
                    {
                        if (ProfileStep.Action == ItineraryServiceActionEnum.Start && _bIsBodyAlreadyTracked && !string.IsNullOrEmpty(_RawMsgBody))
                        {
                            // The raw message body is already recorded in the initialize function so no need to get it here again
                            // this is needed as this is called at the advance event and hence if we track the message body here it will not be the real business message comming at the wire
                            // but will be the message after the execution of the first itinerary step.
                            _bIsBodyAlreadyTracked = false;
                            ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivityReference(BAMStream, ProfileStep.ActivityName, id, dStep.DataName, _RawMsgBody);
                            _RawMsgBody = "";
                        }
                        else
                        {
                            // The message body is not already tracked and hence get it here
                            // (Note that this will get the message at the end of the current itinerary service)
                            // This is a body tracking request so add an activity reference
                            string strMsgBody = "";
                            if (reader.Read())
                            {
                                strMsgBody = reader.ReadOuterXml();
                            }
                            MsgStream.Seek(0, SeekOrigin.Begin);
                            ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivityReference(BAMStream, ProfileStep.ActivityName, id, dStep.DataName, strMsgBody);
                        }
                    }
                    else
                    {
                        // This is to track data extracted using XPath statements
                        if (!string.IsNullOrEmpty(dStep.DataExtractionXPath))
                        {
                            string datavalue = ExtractDataValueXPath(settings, MsgStream, dStep.DataExtractionXPath);
                            if (!string.IsNullOrEmpty(datavalue))
                                ESB.BAM.Wrappers.BAMAPIWrapper.UpdateActivityData(BAMStream, ProfileStep.ActivityName, id, dStep.DataName, datavalue);
                        }
                    }
                }
            }
            if (ProfileStep.Action == ItineraryServiceActionEnum.End)
            {
                // End the activity
                ESB.BAM.Wrappers.BAMAPIWrapper.EndActivity(BAMStream, ProfileStep.ActivityName, id);
            }
            if (MsgStream.Position != 0)
            {
                // Rewind the message stream to make sure we do not break the normal operations
                MsgStream.Seek(0, SeekOrigin.Begin);
            }
            Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.TrackingComponent.TraceOut(callToken);
        }

        /// <summary>
        /// Extracts the data value using the given XPath statement.
        /// </summary>
        /// <param name="settings">The reader settings.</param>
        /// <param name="MsgStream">The message stream.</param>
        /// <param name="MsgXPath">The XPath.</param>
        /// <returns></returns>
        private string ExtractDataValueXPath(XmlReaderSettings settings, Stream MsgStream, string MsgXPath)
        {
            MsgStream.Seek(0, SeekOrigin.Begin);
            XmlReader reader = XmlReader.Create(MsgStream, settings);
            string strValue = null;
            if (!string.IsNullOrEmpty(MsgXPath))
            {
                if (reader.Read())
                {
                    XPathDocument xPathDoc = new XPathDocument(reader);
                    XPathNavigator xNavigator = xPathDoc.CreateNavigator();
                    XPathNodeIterator xNodes = xNavigator.Select(MsgXPath);
                    if (xNodes.Count != 0 && xNodes.MoveNext())
                    {
                        strValue = xNodes.Current.Value;
                    }
                    MsgStream.Seek(0, SeekOrigin.Begin);
                }
            }
            return strValue;
        }

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="BAMItineraryManager"/> class.
        /// </summary>
        public BAMItineraryManager()
            : base()
        {
        }
        #endregion

        #region IItinerary overrides

        /// <summary>
        /// Gets the itinerary step.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns></returns>
        public new Microsoft.Practices.ESB.Itinerary.IItineraryStep GetItineraryStep(Microsoft.BizTalk.Message.Interop.IBaseMessage message)
        {
            Microsoft.Practices.ESB.Itinerary.IItineraryStep step = base.GetItineraryStep(message);
            HandleBAMInitialize(message, DateTime.UtcNow);
            return step;
        }

        /// <summary>
        /// Advance the itinerary step.
        /// </summary>
        /// <param name="message">Current message</param>
        /// <param name="step">Current itinerary step.</param>
        public new void Advance(Microsoft.XLANGs.BaseTypes.XLANGMessage message, Microsoft.Practices.ESB.Itinerary.IItineraryStep step)
        {
            HandleBAMAdvance(message, step, DateTime.UtcNow);
            base.Advance(message, step);
        }

        /// <summary>
        /// Advance the itinerary to the next step.
        /// </summary>
        /// <param name="message">Current message</param>
        /// <param name="context">Current pipeline context.</param>
        /// <param name="step">Current itinerary step.</param>
        public new void Advance(Microsoft.BizTalk.Message.Interop.IBaseMessage message, Microsoft.BizTalk.Component.Interop.IPipelineContext context, Microsoft.Practices.ESB.Itinerary.IItineraryStep step)
        {
            HandleBAMAdvance(message, context, step, DateTime.UtcNow);
            base.Advance(message, context, step);
        }

        /// <summary>
        /// Advance the itinerary to the next step.
        /// </summary>
        /// <param name="message">Current message</param>
        /// <param name="context">Current pipeline context.</param>
        /// <param name="step">Current itinerary step.</param>
        /// <param name="advanceStep">
        /// Determines if the current step should be advanced. In some cases like a send port the 
        /// itinerary should not be advanced on the receive pipeline and is closed out by
        /// the send pipeline.
        /// </param>
        public new void Advance(Microsoft.BizTalk.Message.Interop.IBaseMessage message, Microsoft.BizTalk.Component.Interop.IPipelineContext context, Microsoft.Practices.ESB.Itinerary.IItineraryStep step, bool advanceStep)
        {
            HandleBAMAdvance(message, context, step, DateTime.UtcNow);
            base.Advance(message, context, step, advanceStep);
        }
        #endregion
    }
}
